using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Microblaze.Simulator.Components.Microblaze
{
    public abstract class Instruction
    {
        protected enum BranchInstructionCycleCountStatus
        {
            Undetermined,
            BranchTaken,
            BranchNotTaken
        }

        private uint value;
        private uint address;
        private string symbol;

        public string Symbol
        {
            get { return symbol; }
            set { symbol = value; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public uint Value
        {
            get { return this.value; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public uint Address
        {
            get { return this.address; }
        }

        public string HexAddress
        {
            get { return string.Format("0x{0,8:X8}", address); }
        }

        /// <summary>
        /// Get the opcode (first 6 bits) of this
        /// instruction
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public uint Opcode
        {
            get { return value >> 26; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public uint DestinationRegister
        {
            get { return (value >> 21) & 0x1F; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public uint SourceRegisterA
        {
            get { return (value >> 16) & 0x1F; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public uint SourceRegisterB
        {
            get { return (value >> 11) & 0x1F; }
        }

        [System.ComponentModel.BrowsableAttribute(false)]
        public int ImmediateValue
        {
            get { return (int)((((int)value & 0xFFFF) << 16) >> 16); }
        }

        [System.ComponentModel.BrowsableAttribute(true)]
        public string Mnemonic
        {
            get { return ToString(); }
        }

        public abstract void Execute(MicroblazeComponent MicroblazeComponent);

        public Instruction(string symbol)
        {
            this.symbol = symbol;
        }

        public virtual void Set(uint address, uint value)
        {
            this.address = address;
            this.value = value;
        }

        public abstract int CycleCount
        {
            get;
        }

        protected uint GetImmediateValue(MicroblazeComponent MicroblazeComponent)
        {
            return MicroblazeComponent.ImmediateValueSet ?
              (uint)Get32ImmediateValue(MicroblazeComponent) :
              (uint)ImmediateValue;
        }

        protected uint Get32ImmediateValue(MicroblazeComponent MicroblazeComponent)
        {
            return (uint)((MicroblazeComponent.ImmediateValue << 16) | (ImmediateValue & 0xFFFF));
        }

        public string GetAsBinaryString()
        {
            StringBuilder str = new StringBuilder();

            int i = 31;
            for (; i > 31 - 6; i--)
                str.Append((value >> i) & 1);

            str.Append(" ");

            for (; i > 31 - 11; i--)
                str.Append((value >> i) & 1);

            str.Append(" ");

            for (; i > 31 - 16; i--)
                str.Append((value >> i) & 1);

            str.Append(" ");

            for (; i >= 31 - 21; i--)
                str.Append((value >> i) & 1);

            str.Append(" ");

            for (; i >= 0; i--)
                str.Append((value >> i) & 1);

            return str.ToString();
        }

        public void WriteToConsoleAsBinary()
        {
            Console.WriteLine(GetAsBinaryString());
        }
    }
}
